﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpBolt.Graphics;
using SharpBolt.Time;
using InductBase;
using System.Globalization;
using System.Threading;

namespace SharpBolt.Tweening
{
    public class TweeningManager
    {
        public int MaxAnimators;
        public bool OptionGuaranteedEnded;
        public int OptionImportance;
        public EasingDelegate OptionEasing;
        public bool OptionLooped;

        private List<Animator.PartialKey> OptionPartialKeys;
        public void OptionAddPartialKeyFlag(Animator.PartialKey partialKey)
        {
            if (OptionPartialKeys == null)
                OptionPartialKeys = new List<Animator.PartialKey>();

            if (!OptionPartialKeys.Contains(partialKey))
                OptionPartialKeys.Add(partialKey);
        }


        TimeManager TimeManager;
        List<Animator> Animators;

        public void DropOptions()
        {
            OptionGuaranteedEnded = true;
            OptionImportance = 0;
            OptionEasing = null;
            OptionPartialKeys = null;
            OptionLooped = false;
        }

        public TweeningManager(TimeManager timeManager)
        {
            MaxAnimators = 140;
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
            TimeManager = timeManager;
            Animators = new List<Animator>();
            DropOptions();
        }

        public void Update()
        {
            var CAnimators = new List<Animator>(Animators);

            foreach (var animator in CAnimators)
            {
                //if (animator.GraphicInstance.Hidden)
                //{
                //    animator.Timer.Freeze = true;
                //    continue;                    
                //}
                //else
                //{
                //    animator.Timer.Freeze = false;
                //} 
                
                Animator.AnimationKey key = animator.GetAnimationKey();
                ApplyKey(animator, key);

                if (animator.IsEnded())
                {
                    if (animator.Looped)
                    {
                        animator.Timer.ReStart();
                    }
                    else
                    {
                        animator.OnEnded();
                        animator.Drop();
                    }
                }
            }
            
            Animators.RemoveAll(p => p.GraphicInstance == GraphicInstance.EmptyInstance);
            Animators.RemoveAll(p => p.IsEnded());
        }

        private static void ApplyKey(Animator animator, Animator.AnimationKey key)
        {
            if (animator.PartialKeys == null)
            {
                animator.GraphicInstance.Color = key.DColor.ToColor();
                animator.GraphicInstance.Transformation = key.Transformation;
            }
            else
            {
                if (animator.PartialKeys.Contains(Animator.PartialKey.PositionKey))
                {
                    animator.GraphicInstance.Transformation.Position = key.Transformation.Position;
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.PositionKeyX))
                {
                    animator.GraphicInstance.Transformation.Position =
                         new Vec2(
                            key.Transformation.Position.X,
                            animator.GraphicInstance.Transformation.Position.Y
                            );
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.PositionKeyY))
                {
                    animator.GraphicInstance.Transformation.Position =
                         new Vec2(
                            animator.GraphicInstance.Transformation.Position.X,
                            key.Transformation.Position.Y
                            );
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.ScaleKey))
                {
                    animator.GraphicInstance.Transformation.Scale = key.Transformation.Scale;
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.ScaleKeyX))
                {
                    animator.GraphicInstance.Transformation.Scale =
                        new Vec2(
                            key.Transformation.Scale.X,
                            animator.GraphicInstance.Transformation.Scale.Y
                            );
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.ScaleKeyY))
                {
                    animator.GraphicInstance.Transformation.Scale =
                        new Vec2(
                            animator.GraphicInstance.Transformation.Scale.X,
                            key.Transformation.Scale.Y
                            );
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.RotationKey))
                {
                    animator.GraphicInstance.Transformation.Rotation = key.Transformation.Rotation;
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.ShiftKey))
                {
                    animator.GraphicInstance.Transformation.Shift = key.Transformation.Shift;
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.ShiftKeyX))
                {
                    animator.GraphicInstance.Transformation.Shift = new Vec2(
                        key.Transformation.Shift.X,
                        animator.GraphicInstance.Transformation.Shift.Y);
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.ShiftKeyY))
                {
                    animator.GraphicInstance.Transformation.Shift = new Vec2(
                        animator.GraphicInstance.Transformation.Shift.X,
                        key.Transformation.Shift.Y);
                }

                if (animator.PartialKeys.Contains(Animator.PartialKey.ColorKey))
                {
                    animator.GraphicInstance.Color = key.DColor.ToColor();
                }

            }
        }

        public Animator[] GetAnimators(GraphicInstance graphicInstance)
        {
            var res = Animators.FindAll(p => p.GraphicInstance == graphicInstance);
            if (res == null) return new Animator[0];
            return res.ToArray();
        }

        public bool IsCurrentlyAnimated(GraphicInstance graphicInstance)
        {
            return Animators.Any(p => p.GraphicInstance == graphicInstance);
        }

        public bool IsCurrentlyAnimatedConflicting(GraphicInstance graphicInstance, Animator.PartialKey[] partialKeys)
        {
            var animators = Animators.FindAll(p => p.GraphicInstance == graphicInstance);

            if (animators != null)
            {
                if (partialKeys == null) return true;

                foreach (var animator in animators)
                {
                    if (animator.PartialKeys == null) return true;

                    if (animator.PartialKeys.Any(p => partialKeys.Contains(p)))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public bool IsCurrentlyAnimatedConflicting(Animator newAnimator)
        {
            return IsCurrentlyAnimatedConflicting(newAnimator.GraphicInstance, newAnimator.PartialKeys.ToArray());
        }

        public bool IsAnimatorsConflicting(Animator animatorA, Animator animatorB)
        {
            if (animatorA.GraphicInstance != animatorB.GraphicInstance) return false;

            if (animatorA.PartialKeys == null) return true;
            if (animatorB.PartialKeys == null) return true;

            if (animatorA.PartialKeys.Any(p => animatorB.PartialKeys.Contains(p)))
            {
                return true;
            }

            return false;
        }

        public List<Animator> GetCurrentlyConflictingAnimators(Animator newAnimator)
        {
            List<Animator> result = new List<Animator>();
            var animators = Animators.FindAll(p => p.GraphicInstance == newAnimator.GraphicInstance);

            if (animators.Count > 0)
            {
                if (newAnimator.PartialKeys == null) return animators;

                foreach (var animator in animators)
                {
                    if (animator.PartialKeys == null) return animators;

                    if (animator.PartialKeys.Any(p => newAnimator.PartialKeys.Contains(p)))
                    {
                        result.Add(animator);
                    }


                }
            }
            return result;
        }

        public Animator Animate(GraphicInstance graphicInstance, Animator.AnimationKey animationKeyA, Animator.AnimationKey animationKeyB, long milliseconds)
        {
            Animator animator = new Animator();

            animator.GraphicInstance = graphicInstance;
            animator.Timer = TimeManager.CreateTimer();
            animator.Timer.Start(milliseconds);

            animator.AnimationKeyA = animationKeyA;
            animator.AnimationKeyB = animationKeyB;

            animator.guaranteedEnded = OptionGuaranteedEnded;
            animator.importance = OptionImportance;
            animator.easing = OptionEasing;

            animator.PartialKeys = OptionPartialKeys;
            animator.Looped = OptionLooped;

            if (animator.PartialKeys.Contains(Animator.PartialKey.ScaleKey))
            {
                animator.PartialKeys.Add(Animator.PartialKey.ScaleKeyX);
                animator.PartialKeys.Add(Animator.PartialKey.ScaleKeyY);
            }

            if (animator.PartialKeys.Contains(Animator.PartialKey.PositionKey))
            {
                animator.PartialKeys.Add(Animator.PartialKey.PositionKeyX);
                animator.PartialKeys.Add(Animator.PartialKey.PositionKeyY);
            }

            if (animator.PartialKeys.Contains(Animator.PartialKey.ShiftKey))
            {
                animator.PartialKeys.Add(Animator.PartialKey.ShiftKeyX);
                animator.PartialKeys.Add(Animator.PartialKey.ShiftKeyY);
            }

            DropOptions();

            var conflicts = GetCurrentlyConflictingAnimators(animator);

            if (conflicts.Count != 0)
            {
                foreach (var conflict in conflicts)
                {
                    if (animator.importance < conflict.importance) return null;

                    if (conflict.guaranteedEnded)
                    {
                        Animator.AnimationKey key = conflict.GetEndKey();
                        ApplyKey(animator, key);
                    }

                    conflict.Drop();
                    Animators.Remove(conflict);
                }
            }

            //if (Animators.Count > MaxAnimators)
            //{
            //    Animators.RemoveAt(0);
            //}

            Animators.Add(animator);

            return animator;
        }

        public Animator AnimateTo(GraphicInstance graphicInstance, Transformation transformation, Color color, long milliseconds)
        {
            Animator.AnimationKey animationKeyA = new Animator.AnimationKey()
            {
                Transformation = graphicInstance.Transformation.Copy(),
                DColor = graphicInstance.Color.ToDColor()
            };

            Animator.AnimationKey animationKeyB = new Animator.AnimationKey()
            {
                Transformation = transformation.Copy(),
                DColor = color.ToDColor()
            };

            return Animate(graphicInstance, animationKeyA, animationKeyB, milliseconds);
        }

        public Animator Animate(GraphicInstance graphicInstance, Transformation transformationA, Transformation transformationB, long milliseconds)
        {
            Animator.AnimationKey animationKeyA = new Animator.AnimationKey()
            {
                Transformation = transformationA.Copy(),
                DColor = graphicInstance.Color.ToDColor()
            };

            Animator.AnimationKey animationKeyB = new Animator.AnimationKey()
            {
                Transformation = transformationB.Copy(),
                DColor = graphicInstance.Color.ToDColor()
            };

            return Animate(graphicInstance, animationKeyA, animationKeyB, milliseconds);
        }

        public Animator Animate(GraphicInstance graphicInstance, DColor DColorA, DColor DColorB, long milliseconds)
        {

            Animator.AnimationKey animationKeyA = new Animator.AnimationKey()
            {
                Transformation = graphicInstance.Transformation,
                DColor = DColorA,
            };

            Animator.AnimationKey animationKeyB = new Animator.AnimationKey()
            {
                Transformation = graphicInstance.Transformation,
                DColor = DColorB,
            };

            OptionAddPartialKeyFlag(Animator.PartialKey.ColorKey);
            OptionAddPartialKeyFlag(Animator.PartialKey.ColorKey);

            return Animate(graphicInstance, animationKeyA, animationKeyB, milliseconds);
        }

        public Animator Animate(GraphicInstance graphicInstance, Color ColorA, Color ColorB, long milliseconds)
        {
            return Animate(graphicInstance, ColorA.ToDColor(), ColorB.ToDColor(), milliseconds);
        }

        public Animator Animate(GraphicInstance graphicInstance, string description, int time = -1, Action onEnd = null)
        {
            //! ifNoConflict

            //Color
            //c[R,G,B]
            //c[yellow]
            //c[A,R,G,B]

            //Position
            //p[x,y]

            //Scale
            //s[x,y]

            //Rotation
            //r<number>

            //Shift
            //f[x,y]

            //> between animations
            //: 

            //Time
            // millis

            //:
            //Flags
            //ge        granted end
            //i<number> importance
            //eSin      easing
            //pp        ping-pong 

            bool ifNoConflict = false;
            if (description[0] == '!')
            {
                ifNoConflict = true;
                description = description.Remove(0, 1);
            }

            var blocks = description.Split(new[] { ':' });

            if (blocks.Length < 2) return null;



            var nodes = blocks[0].Split(new[] { '>' });
            if (nodes.Length < 2) return null;

            //node A
            var transDesc = nodes[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            Transformation transA = graphicInstance.Transformation.Copy();
            Color colorA = graphicInstance.Color;
            colorA = ParseFlags(transDesc, transA, colorA, graphicInstance);

            //node B
            transDesc = nodes[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            Transformation transB = graphicInstance.Transformation.Copy();
            Color colorB = graphicInstance.Color;
            colorB = ParseFlags(transDesc, transB, colorB, graphicInstance);

            if (time < 0)
                if (!Int32.TryParse(blocks[1], out time))
                {
                    time = 500;
                }

            OptionEasing = Easing.EaseLinear;

            if (blocks.Length > 2)
            {
                var options = blocks[2].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var flag in options)
                {
                    if (flag.Length < 2) continue;

                    if (flag.StartsWith("i"))
                    {
                        OptionImportance = Int32.Parse(flag.Remove(0, 1));
                    }

                    if (flag == "ge")
                    {
                        OptionGuaranteedEnded = true;
                    }

                    if (flag == "pp")
                    {
                        OptionEasing = Easing.PingPongLinear;
                    }

                    if (flag.Length < 3) continue;

                    if (flag.StartsWith("e"))
                    {
                        string easingName = (flag.Remove(0, 1));

                        if (Easing.EasingDictionary.ContainsKey(easingName))
                            OptionEasing = Easing.EasingDictionary[easingName];
                    }

                    if (flag.StartsWith("pp["))
                    {
                        string[] args = flag.Remove(0, 2).Split(new[] { '[', ']', ',' }, StringSplitOptions.RemoveEmptyEntries);
                        if (args.Length < 2) continue;

                        string easeName1 = args[0].Remove(0, 1);
                        string easeName2 = args[1].Remove(0, 1);
                        double part = 0.5;

                        EasingDelegate ease1 = Easing.EaseLinear;
                        EasingDelegate ease2 = Easing.EaseLinear;

                        if (Easing.EasingDictionary.ContainsKey(easeName1))
                            ease1 = Easing.EasingDictionary[easeName1];

                        if (Easing.EasingDictionary.ContainsKey(easeName2))
                            ease2 = Easing.EasingDictionary[easeName2];

                        if (args.Length > 2)
                            part = Double.Parse(args[2]);

                        OptionEasing = Easing.PingPong(ease1, ease2, part);
                    }

                    if (flag.StartsWith("loop"))
                    {
                        OptionLooped = true;
                    }


                }

            }

            if (ifNoConflict)
            {
                if (IsCurrentlyAnimatedConflicting(graphicInstance, OptionPartialKeys.ToArray()))
                {
                    DropOptions();
                    return null;
                }
            }

            Animator.AnimationKey animationKeyA = new Animator.AnimationKey()
            {
                Transformation = transA.Copy(),
                DColor = colorA.ToDColor()
            };

            Animator.AnimationKey animationKeyB = new Animator.AnimationKey()
            {
                Transformation = transB.Copy(),
                DColor = colorB.ToDColor()
            };

            var animator = Animate(graphicInstance, animationKeyA, animationKeyB, (long)time);
            if (animator != null && onEnd != null)
            {
                animator.Ended += onEnd;
            }
            return animator;
        }

        private Color ParseFlags(string[] transDesc, Transformation trans, Color color, GraphicInstance graphicInstance)
        {
            foreach (var flag in transDesc)
            {
                if (flag.Length < 2) continue;

                if (flag.StartsWith("r#def"))
                {
                    trans.Rotation = graphicInstance.Transformation.Rotation;
                    OptionAddPartialKeyFlag(Animator.PartialKey.RotationKey);
                }

                if (flag.StartsWith("r"))
                {
                    trans.Rotation = Double.Parse(flag.Remove(0, 1));
                    OptionAddPartialKeyFlag(Animator.PartialKey.RotationKey);
                }

                if (flag.Length < 3) continue;

                if (flag.StartsWith("p#def"))
                {
                    trans.Position = graphicInstance.Transformation.Position;
                    OptionAddPartialKeyFlag(Animator.PartialKey.PositionKey);
                }

                if (flag.StartsWith("p["))
                {
                    trans.Position = Vec2.Parse(flag.Remove(0, 1));
                    OptionAddPartialKeyFlag(Animator.PartialKey.PositionKey);
                }

                if (flag.StartsWith("ap["))
                {
                    trans.Position = graphicInstance.Transformation.Position + Vec2.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.PositionKey);
                }

                if (flag.StartsWith("s#def"))
                {
                    trans.Scale = graphicInstance.Transformation.Scale;
                    OptionAddPartialKeyFlag(Animator.PartialKey.ScaleKey);
                }

                if (flag.StartsWith("s["))
                {
                    trans.Scale = Vec2.Parse(flag.Remove(0, 1));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ScaleKey);
                }


                if (flag.StartsWith("as["))
                {
                    trans.Scale = graphicInstance.Transformation.Scale * Vec2.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ScaleKey);
                }

                if (flag.StartsWith("f["))
                {
                    trans.Shift = Vec2.Parse(flag.Remove(0, 1));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ShiftKey);
                }

                if (flag.StartsWith("c#def"))
                {
                    color = graphicInstance.Color;
                    OptionAddPartialKeyFlag(Animator.PartialKey.ColorKey);
                }

                if (flag.StartsWith("c["))
                {
                    color = Color.Parse(flag.Remove(0, 1));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ColorKey);
                }


                //============

                if (flag.StartsWith("px"))
                {
                    trans.Position.X = Double.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.PositionKeyX);
                }

                if (flag.StartsWith("py"))
                {
                    trans.Position.Y = Double.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.PositionKeyY);
                }

                if (flag.StartsWith("apx"))
                {
                    trans.Position.X = graphicInstance.Transformation.Position.X + Double.Parse(flag.Remove(0, 3));
                    OptionAddPartialKeyFlag(Animator.PartialKey.PositionKeyX);
                }

                if (flag.StartsWith("apy"))
                {
                    trans.Position.Y = graphicInstance.Transformation.Position.Y + Double.Parse(flag.Remove(0, 3));
                    OptionAddPartialKeyFlag(Animator.PartialKey.PositionKeyY);
                }

                if (flag.StartsWith("sx"))
                {
                    trans.Scale.X = Double.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ScaleKeyX);
                }

                if (flag.StartsWith("sy"))
                {
                    trans.Scale.Y = Double.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ScaleKeyY);
                }

                if (flag.StartsWith("fx"))
                {
                    trans.Shift.X = Double.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ShiftKeyX);
                }

                if (flag.StartsWith("fy"))
                {
                    trans.Shift.Y = Double.Parse(flag.Remove(0, 2));
                    OptionAddPartialKeyFlag(Animator.PartialKey.ShiftKeyY);
                }
            }
            return color;
        }
    }
}
